﻿using System;
using System.Drawing;
using System.Xml;
namespace GeoSketch2.Core
{
    /// <summary>
    /// Indicates the direction of the Y axis
    /// </summary>
    public enum YAxisDirection
    {
        /// <summary>
        /// The Y axis is upside
        /// </summary>
        UpSide,
        /// <summary>
        /// The Y axis is downside
        /// </summary>
        DownSide
    }

    /// <summary>
    /// A rectangle with double precision values
    /// </summary>
    [Serializable]
    public class RectangleD
    {
        #region XMLElementsName
#pragma warning disable 1591
        public const string XML_UpperLeftPoint = "UpperLeftPoint";
        public const string XML_Width = "Width";
        public const string XML_Height = "Height";
        public const string XML_YaxisDirection = "YaxisDirection";
#pragma warning restore 1591
        #endregion

        #region Fields
        /// <summary>
        /// The X-coordinate of the upper left corner
        /// </summary>
        public double X;
        /// <summary>
        /// The Y-coordinate of the upper left corner
        /// </summary>
        public double Y;
        /// <summary>
        /// The width of the rectangle
        /// </summary>
        public double Width;
        /// <summary>
        /// The height of the rectangle
        /// </summary>
        public double Height;
        /// <summary>
        /// The direction of the Y axis
        /// </summary>
        private YAxisDirection _YaxisDirection;
        #endregion

        #region Constructor
        /// <summary>
        /// A rectangle with double precision values
        /// </summary>
        public RectangleD()
            : this(0, 0, 1, 1)
        {
        }

        /// <summary>
        /// A rectangle with double precision values
        /// </summary>
        /// <param name="YDirection">The direction of the Y axis</param>
        public RectangleD(YAxisDirection YDirection)
            : this(0, 0, 1, 1, YDirection)
        {
        }

        /// <summary>
        /// A rectangle with double precision values
        /// </summary>
        /// <param name="X">The X-coordinate of the upper left corner</param>        
        /// <param name="Y">The Y-coordinate of the upper left corner</param>
        /// <param name="Width">The width of the rectangle</param>        
        /// <param name="Height">The height of the rectangle</param>
        public RectangleD(double X, double Y, double Width, double Height)
            : this(X, Y, Width, Height, YAxisDirection.DownSide)
        {
        }

        /// <summary>
        /// A rectangle with double precision values
        /// </summary>
        /// <param name="X">The X-coordinate of the upper left corner</param>        
        /// <param name="Y">The Y-coordinate of the upper left corner</param>
        /// <param name="Width">The width of the rectangle</param>        
        /// <param name="Height">The height of the rectangle</param>
        /// <param name="YAxisDirection">The direction of the Y axis</param>
        public RectangleD(double X, double Y, double Width, double Height, YAxisDirection YAxisDirection)
        {
            this.X = X;
            this.Y = Y;
            this.Width = Width;
            this.Height = Height;
            this._YaxisDirection = YAxisDirection;
        }

        /// <summary>
        /// A rectangle with double precision values
        /// </summary>
        /// <param name="Xr">An XmlReader on the start element RectangleD</param>
        public RectangleD(XmlReader Xr)
        {
            Xr.ReadToFollowing(XML_UpperLeftPoint);
            Xr.ReadToFollowing(typeof(Point2D64D).FullName);
            UpperLeftPoint = new Point2D64D(Xr);
            Xr.ReadToFollowing(XML_Width);
            Width = Convert.ToDouble(Xr.ReadString(), Vars.Setup.GeoSketch.NumberFormat);
            Xr.ReadToFollowing(XML_Height);
            Height = Convert.ToDouble(Xr.ReadString(), Vars.Setup.GeoSketch.NumberFormat);
            Xr.ReadToFollowing(XML_YaxisDirection);
            YaxisDirection = (YAxisDirection)Enum.Parse(typeof(YAxisDirection), Xr.ReadString());

            Xr.ReadEndElement();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Returns an array of 4 points which contains the 4 corners
        /// of the rectangle, from the upper left one, clockwise
        /// </summary>
        public Point2D64D[] ToArray()
        {
            return new Point2D64D[]{
                UpperLeftPoint,
                UpperRightPoint,
                LowerRightPoint,
                LowerLeftPoint
            };
        }

        /// <summary>
        /// Gets a rectangle from its center and its dimension
        /// </summary>
        /// <param name="Center">The center of the rectangle</param>        
        /// <param name="Width">The width of the rectangle</param>        
        /// <param name="Height">The height of the rectangle</param>
        /// <param name="Ydirection">The direction of the Y axis</param>
        public static RectangleD FromCenter(Point2D64D Center, double Width, double Height, YAxisDirection Ydirection)
        {
            if (Ydirection == YAxisDirection.DownSide)
            {
                return new RectangleD(Center.X - Width / 2, Center.Y - Height / 2, Width, Height, Ydirection);
            }
            else
            {
                return new RectangleD(Center.X - Width / 2, Center.Y + Height / 2, Width, Height, Ydirection);
            }
        }

        /// <summary>
        /// Saves the object to the xml format
        /// </summary>
        /// <param name="Xw">An XmlWriter to write to</param>
        public void ToFile(XmlWriter Xw)
        {
            Xw.WriteStartElement(this.GetType().FullName);

            Xw.WriteStartElement(XML_UpperLeftPoint);
            UpperLeftPoint.ToFile(Xw);
            Xw.WriteEndElement();
            Xw.WriteStartElement(XML_Width);
            Xw.WriteString(Width.ToString(Vars.Setup.GeoSketch.NumberFormat));
            Xw.WriteEndElement();
            Xw.WriteStartElement(XML_Height);
            Xw.WriteString(Height.ToString(Vars.Setup.GeoSketch.NumberFormat));
            Xw.WriteEndElement();
            Xw.WriteStartElement(XML_YaxisDirection);
            Xw.WriteString(YaxisDirection.ToString());
            Xw.WriteEndElement();

            Xw.WriteEndElement();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Return the upper left point of the rectangle
        /// </summary>
        public Point2D64D UpperLeftPoint
        {
            get
            {
                return new Point2D64D(X, Y);
            }
            set
            {
                X = value.X;
                Y = value.Y;
            }
        }

        /// <summary>
        /// Return the upper right point of the rectangle
        /// </summary>
        public Point2D64D UpperRightPoint
        {
            get
            {
                return new Point2D64D(X + Width, Y);
            }
        }

        /// <summary>
        /// Return the lower right point of the rectangle
        /// </summary>
        public Point2D64D LowerRightPoint
        {
            get
            {
                if (YaxisDirection == YAxisDirection.DownSide)
                {
                    return new Point2D64D(X + Width, Y + Height);
                }
                else
                {
                    return new Point2D64D(X + Width, Y - Height);
                }
            }
        }

        /// <summary>
        /// Return the lower left point of the rectangle
        /// </summary>
        public Point2D64D LowerLeftPoint
        {
            get
            {
                if (YaxisDirection == YAxisDirection.DownSide)
                {
                    return new Point2D64D(X, Y + Height);
                }
                else
                {
                    return new Point2D64D(X, Y - Height);
                }
            }
        }

        /// <summary>
        /// Return the central point of the rectangle
        /// </summary>
        public Point2D64D Center
        {
            get
            {
                if (YaxisDirection == YAxisDirection.DownSide)
                {
                    return new Point2D64D(X + Width / 2, Y + Height / 2);
                }
                else
                {
                    return new Point2D64D(X + Width / 2, Y - Height / 2);
                }
            }
        }

        /// <summary>
        /// Gets or sets the direction of the Y axis
        /// </summary>
        public YAxisDirection YaxisDirection
        {
            get
            {
                return _YaxisDirection;
            }
            set
            {
                if (_YaxisDirection == YAxisDirection.DownSide && value == YAxisDirection.UpSide)
                {
                    _YaxisDirection = value;
                    Y = Y + Height;
                }
                else if (_YaxisDirection == YAxisDirection.UpSide && value == YAxisDirection.DownSide)
                {
                    _YaxisDirection = value;
                    Y = Y - Height;
                }
            }
        }
        #endregion

        #region Operators
        /// <summary>
        /// Converts a rectangle with integer coordinates to a rectangle with double precision coordinates
        /// </summary>
        /// <param name="Rectangle">Source rectangle</param>
        public static implicit operator RectangleD(Rectangle Rectangle)
        {
            return new RectangleD(Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height);
        }

        /// <summary>
        /// Converts a rectangle with single precision coordinates to a rectangle with double precision coordinates
        /// </summary>
        /// <param name="Rectangle">Source rectangle</param>
        public static implicit operator RectangleD(RectangleF Rectangle)
        {
            return new RectangleD(Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height);
        }

        /// <summary>
        /// Converts a rectangle with double precision coordinates to a rectangle with integer precision coordinates
        /// </summary>
        /// <param name="Rectangle">Source rectangle</param>
        public static implicit operator Rectangle(RectangleD Rectangle)
        {
            return new Rectangle((int)(Math.Round(Rectangle.X)), (int)(Math.Round(Rectangle.Y)), (int)(Math.Round(Rectangle.Width)), (int)(Math.Round(Rectangle.Height)));
        }

        /// <summary>
        /// Converts a rectangle with double precision coordinates to a rectangle with integer precision coordinates
        /// </summary>
        /// <param name="Rectangle">Source rectangle</param>
        public static implicit operator RectangleF(RectangleD Rectangle)
        {
            return new RectangleF((float)Rectangle.X, (float)Rectangle.Y, (float)Rectangle.Width, (float)Rectangle.Height);
        }

        /// <summary>
        /// Gets the union of two rectangles
        /// </summary>
        /// <param name="Rectangle1">First rectangle</param>
        /// <param name="Rectangle2">Second rectangle</param>
        public static RectangleD operator +(RectangleD Rectangle1, RectangleD Rectangle2)
        {
            return Tools.GetRectangleDBounds(new Point2D64D[]{Rectangle1.UpperLeftPoint,Rectangle1.UpperRightPoint,Rectangle1.LowerRightPoint,Rectangle1.LowerLeftPoint,
            Rectangle2.UpperLeftPoint,Rectangle2.UpperRightPoint,Rectangle2.LowerRightPoint,Rectangle2.LowerLeftPoint}, Rectangle1.YaxisDirection);
        }
        #endregion
    }
}
